

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.json.JSONArray;
import org.json.JSONObject;

public class SBWSController {
	
	private static String BASE_URL = "http://manae.de/sb.php?";
	
	public Boolean registerUser(String number, String name, String email) throws SBException {
		try {
			String hash = hashAsMd5(number);
			String[] keys = new String[] { "access", "action", "name", "email", "number" };
			String[] values = new String[] { "shared_bucks", "registerUser", name, email, hash };
			List<NameValuePair> l = buildParameterList(keys, values);
			String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
			String content = doHTTPGETToString(url);
			JSONObject o = new JSONObject(content);
			return  o.getBoolean("result");
		} catch(Exception e) {
			throw new SBException(e);
		}
	}
	
	public Boolean isUser(String number) throws SBException {
		try {
			String hash = hashAsMd5(number);
			String[] keys = new String[] { "access", "action", "number" };
			String[] values = new String[] { "shared_bucks", "isUser", hash };
			List<NameValuePair> l = buildParameterList(keys, values);
			String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
			String content = doHTTPGETToString(url);
			JSONObject o = new JSONObject(content);
			return  o.getBoolean("result");
		} catch(Exception e) {
			throw new SBException(e);
		}		
	}
	
	public Boolean makePayment(String number, Double amount, String desc) throws SBException {
		try {
			String hash = hashAsMd5(number);
			String[] keys = new String[] { "access", "action", "number", "amount", "desc" };
			String[] values = new String[] { "shared_bucks", "makePayment", hash, amount.toString(), desc };
			List<NameValuePair> l = buildParameterList(keys, values);
			String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
			String content = doHTTPGETToString(url);
			JSONObject o = new JSONObject(content);
			return  o.getBoolean("result");
		} catch(Exception e) {
			throw new SBException(e);
		}			
	}
	
	public Boolean participate(String paymentId, String number) throws SBException {
		try {
			String hash = hashAsMd5(number);
			String[] keys = new String[] { "access", "action", "number", "payment" };
			String[] values = new String[] { "shared_bucks", "participate", hash, paymentId};
			List<NameValuePair> l = buildParameterList(keys, values);
			String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
			String content = doHTTPGETToString(url);
			JSONObject o = new JSONObject(content);
			return  o.getBoolean("result");
		} catch(Exception e) {
			throw new SBException(e);
		}			
	}	
	
	public List<String> getFellowUsers(List<String> fellowNumbers) throws SBException {
		try {
			String[] keys = new String[] { "access", "action",  };
			String[] values = new String[] { "shared_bucks", "checkFellowUsers" };
			List<NameValuePair> l = buildParameterList(keys, values);
			String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
			
			// Generate MD5 hash of each number
			List<String> hashList = new ArrayList<String>();
			for(String number : fellowNumbers) {
				hashList.add(hashAsMd5(number));
			}
			
			// Generate JSon object		
			JSONArray array = new JSONArray(hashList);
			
			String content = doHTTPPOSTToString(url, "result", array);
			
			// Get Number array
			JSONObject o = new JSONObject(content);
			JSONArray resultArray = o.getJSONArray("result");
			
			// Return List with fellow users
			List<String> users = new ArrayList<String>(resultArray.length());
			for(int i=0; i<resultArray.length(); i++) {
				users.add(resultArray.getString(i));
			}
			
			return users;
		} catch(Exception e) {
			throw new SBException(e);
		}	
	}
	
	public Set<SBPayment> invoice(String number) throws SBException {
		try {
			String hash = hashAsMd5(number);
			String[] keys = new String[] { "access", "action", "number" };
			String[] values = new String[] { "shared_bucks", "invoice", hash };
			List<NameValuePair> l = buildParameterList(keys, values);
			String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
			
			String content = doHTTPGETToString(url);
			
			JSONObject o = new JSONObject(content);
			JSONObject resultObject = o.getJSONObject("result");
			JSONArray paymentArray = resultObject.getJSONArray("payments");
			
			Map<String, SBPayment> paymentMap = new HashMap<String, SBPayment>();
			
			// Parse Payments array
			for(int i=0; i<paymentArray.length(); i++) {
				JSONObject jsonPayment = paymentArray.getJSONObject(i).getJSONObject("payment");
				String id = jsonPayment.getString("id");
				String participant = jsonPayment.getString("participant");
				
				SBPayment p = null;
				
				if(paymentMap.containsKey(id)) {
					p = paymentMap.get(id);
				} else {
					p = new SBPayment(id);
					p.setAmount(jsonPayment.getDouble("amount"));
					p.setClosed(jsonPayment.getInt("closed") == 0 ? false : true);
					p.setDate(new Date(jsonPayment.getLong("timestamp")));
					p.setDescription(jsonPayment.getString("description"));
					
					paymentMap.put(id, p);
				}
				
				p.getParticipants().add(participant);
			}
			
			// Return Set<SBPayment>
			Set<SBPayment> paymentSet = new HashSet<SBPayment>();
			for(String key : paymentMap.keySet())
				paymentSet.add(paymentMap.get(key));
			
			return paymentSet;
			
		} catch(Exception e) {
			throw new SBException(e);
		}			
	}
	
	private static String hashAsMd5(String input) throws NoSuchAlgorithmException {		
	    String result = input;
	    if(input != null) {
	        MessageDigest md = MessageDigest.getInstance("MD5"); //or "SHA-1"
	        md.update(input.getBytes());
	        BigInteger hash = new BigInteger(1, md.digest());
	        result = hash.toString(16);
	        while(result.length() < 32) {
	            result = "0" + result;
	        }
	    }
	    return result;		
	}
	
	private static List<NameValuePair> buildParameterList(String[] keys, String[] values) {
		assert(keys.length == values.length);
		List<NameValuePair> list = new ArrayList<NameValuePair>();
		for(int i=0; i<keys.length; i++) {
			NameValuePair p1 = new BasicNameValuePair(keys[i], values[i]);
			list.add(p1);
		}
		return list;
	}
	
	private String doHTTPPOSTToString(String url, String param, Object o) throws SBException {		
		try {
			StringBuilder builder = new StringBuilder();
			HttpParams params = new BasicHttpParams();
		    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
		    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
			HttpClient client = new DefaultHttpClient(params);
			HttpPost httpPost = new HttpPost(url);
			
			// Add POST variables
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
		    nameValuePairs.add(new BasicNameValuePair("result", o.toString()));
		    httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
		    
			HttpResponse response = client.execute(httpPost);
			StatusLine statusLine = response.getStatusLine();
			int statusCode = statusLine.getStatusCode();
			if (statusCode == 200) {
				HttpEntity entity = response.getEntity();
				InputStream content = entity.getContent();
				BufferedReader reader = new BufferedReader(new InputStreamReader(content));
				String line;
				while ((line = reader.readLine()) != null) {
					builder.append(line);
				}
			} else {
				throw new SBException("Cannot to connect/download via HTTP GET");
			}
			return builder.toString();
		} catch(Exception e) {
			throw new SBException(e);
		}	
		
	}

	private String doHTTPGETToString(String url) throws SBException {
		try {
			StringBuilder builder = new StringBuilder();
			HttpClient client = new DefaultHttpClient();
			HttpGet httpGet = new HttpGet(url);
			
			HttpResponse response = client.execute(httpGet);
			StatusLine statusLine = response.getStatusLine();
			int statusCode = statusLine.getStatusCode();
			if (statusCode == 200) {
				HttpEntity entity = response.getEntity();
				InputStream content = entity.getContent();
				BufferedReader reader = new BufferedReader(new InputStreamReader(content));
				String line;
				while ((line = reader.readLine()) != null) {
					builder.append(line);
				}
			} else {
				throw new SBException("Cannot to connect/download via HTTP GET");
			}
			return builder.toString();
		} catch(Exception e) {
			throw new SBException(e);
		}
	}
}
